package test.excelHttpsample;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.jmeter.assertions.Assertion;
import org.apache.jmeter.assertions.AssertionResult;
import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.ConfigTestElement;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.engine.util.ConfigMergabilityIndicator;
import org.apache.jmeter.engine.util.NoConfigMerge;
import org.apache.jmeter.processor.PostProcessor;
import org.apache.jmeter.processor.PreProcessor;
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
import org.apache.jmeter.report.core.Sample;
import org.apache.jmeter.samplers.AbstractSampler;
import org.apache.jmeter.samplers.Entry;
import org.apache.jmeter.samplers.SampleListener;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.TestElementProperty;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterThread;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.threads.SamplePackage;
import org.apache.jmeter.threads.TestCompiler;
import org.apache.jmeter.timers.Timer;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.oro.text.MalformedCachePatternException;
import org.apache.oro.text.PatternCacheLRU;
import org.apache.oro.text.regex.MatchResult;
import org.apache.oro.text.regex.PatternMatcherInput;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;




import test.ExcelUtil;
import test.JDBCassertion.JDBCAssertion;

public class HttpBaseTestSample extends AbstractSampler //implements ActionListener
{
	
	public static final Logger log=LoggerFactory.getLogger(HttpBaseTestSample.class);
	public static final long serialVersionUID=240L;
	private static AtomicInteger classCount=new AtomicInteger(0);
	
	 public static final String parentPath = "parentPath"; //$NON-NLS-1$
	 public static final String fileName = "fileName"; //$NON-NLS-1$
	// public static final boolean isSelected = false;
	 public static final String isChoose = "ischoosed";
	 
	
	private Set<String> store;//用于存储失败的用例序号
	 private  List<String> currentRow;
	 private HTTPSamplerProxy subSample;
	private SampleResult subResult ;
	private JMeterContext jMeterContext;
	private SamplePackage samplePackage;
	private SampleResult sampleResult;
	private String body;
	private StringBuilder resReponse;//用来记录错误的用例
	private AssertionResult assertionResult;
	
	 private ExcelUtil excelUtil;
	  private static final Set<String> APPLIABLE_CONFIG_CLASSES = new HashSet<>(
	            Arrays.asList(
	                  
	                    "test.TestSampleDefaultConfigGui"
	            ));
	private static final String ConfigTestElement = null;
	
	public  boolean isIsselected() {
		return this.getPropertyAsBoolean(this.isChoose);
	}
	
	public void setIsSelect(boolean isSelected){
		this.setProperty(this.isChoose, isSelected);
	}

	public String getA() {
		return  getPropertyAsString(parentPath);
	}


	public void setA(String parentPath) {
		this.setProperty(this.parentPath, parentPath);
	}


	public String getB() {
		return getPropertyAsString(fileName);
	}


	public void setB(String fileName) {
		this.setProperty(this.fileName, fileName);
	}





	
	
	public HttpBaseTestSample() {
		classCount.incrementAndGet();//访问一次加一下，几个线程就有几次
		trace("HttpBaseTestSample");
		
	}
	

	/**
	 * 实现后的模式，依赖的接口出错，则此接口报错
	 * 实现从excel中读取数据，并传入到subSample中
	 * 1.获取excel的file
	 * 2.获取里面的数据，并取出一行
	 * 3.判断是否有依赖，有依赖的情况下判断依赖的用例是否执行正确，正确则进行，错误则跳到57，记录错误结果
	 * 4.进行参数化
	 * 5.将一行的数据导入到subSmple中
	 * 6.将脚本相关配置传入到subSample中
	 * 7.获取到subResult,并进行断言
	 * 8.断言成功的进行参数化提取
	 * 9.记录行号和测试结果并添加到mainSample，以及map中，作为第3步依赖参考
	 * 10.
	 */
	
	/**
	 * @time:20170419
	 * 给此插件增加一个调试功能
	 * 1.excel第一列为序号列，如果第一列为#开头则为注释，以$开头则表示此行用例要进行调试
	 * 2.在进行执行用例前，判断是否是调试状态，如果是，刚对第一列进行扫描，查看是否存在$，对存在$标志符的用例进行执行
	 */
	@Override
	public SampleResult sample(Entry a){
		resReponse=new StringBuilder();
		sampleResult = new SampleResult();
		int rightSample=0;
		int wrongSample=0;
		store=new HashSet<>();
		sampleResult.setSampleLabel(this.getName());
		sampleResult.sampleStart();
		jMeterContext = getThreadContext();
		
		//JMeterContextService.getContext().getVariables();这里设置的是全局变量，跨线程,
		//看起来也不一定,,Gives access to the current thread context
		//JMeterUtils.getJMeterProperties();这里全局变量
		//getThreadContext().getVariables();这里的不是跨线程  Returns the threadContext.
		samplePackage = (SamplePackage) jMeterContext.getVariables().getObject(JMeterThread.PACKAGE_OBJECT);
		trace("parctiseDemo01Sample");
		try {
			//System.out.println(getB()+"+++++++++++++++++++++++++");
			excelUtil = new ExcelUtil(getB());
			excelUtil.setHasNext(1);//表示从第二行开始读
			while(excelUtil.hasNext()){
				currentRow = excelUtil.readRow(excelUtil.getHasNext()-1);//读取一行
				if (isIsselected()) {//如果为真则调试模式
					if (!currentRow.get(0).startsWith("$")) {
						continue;
					}
					
				}
				
				if (currentRow.get(0).startsWith("#")) {//如果以#开头，代表这一行为注释，跳过
					
					continue;
				}
				/**
				 * 判断依赖是否有依赖，并且判断依赖的用例是否正确
				 * 如果正确正常执行
				 * 如果错误则不执行，sample()方法，直接进行判断是否正确
				 */
				if(!store.contains(currentRow.get(11))){//不包含时，代表依赖用例正确或者无依赖
					currentRow = replaceParam(currentRow, jMeterContext.getVariables());//替换所有参数化参数
					subSample = new HTTPSamplerProxy();
					
				
					/**对subsample进行赋值，模拟jmeterGUI将各种值赋给httpsampleProxy，
					 * 以及模拟jmeter将各种config赋给subSample
					 * 另外将sample中的headerManage替换为excel中的contentType
					 */
					re_setSubSampleParams();
					
					/**
					 * 这里可能要try catch一下，以防异常
					 * 具体是否要可能要具体实验一下是不是会抛出异常
					 * 
					 */
					subResult = subSample.sample(null);//执行sample，并获取subResult
					/**
					 * 开始进行断言，这里仅支持包含关系,后续还要支持sql断言，暂时没有读断言类型
					 */
					if (currentRow.get(10)!=null&&!currentRow.get(10).isEmpty()) {
						
						assertion(subResult,currentRow.get(9),currentRow.get(10));
					}
					
				}else{//用例依赖错误,给result注入一些基础信息，及错误信息
					subResult=new HTTPSampleResult();
					subResult.setSampleLabel(currentRow.get(0)+"、"+currentRow.get(1));//设置每一行的名字
					subResult.setThreadName(jMeterContext.getThread().getThreadName());
					subResult.setSuccessful(false);
					subResult.setResponseCode("500");
					subResult.setResponseData("依赖错误","utf-8");
					subResult.setResponseMessage("依赖错误");
					
				}
				
				/**
				 * 接下来判断断言是否成功，如果成功，刚要执行正则，提取新的参数，并存储
				 * 如果失败刚存到一个map中
				 */
				
				if (subResult.isSuccessful()) {
					rightSample++;
					/**
					 * 进行参数化提取，尝试调用正则提取
					 */
					if(currentRow.get(12)!=null&&!currentRow.isEmpty()){
						
						process(subResult.getResponseDataAsString(), currentRow.get(12), currentRow.get(13));
					}
					
				}else {
					wrongSample++;
					store.add(currentRow.get(0));//记录错误的用例，以便于判断依赖
					
					resReponse.append("\r\n"+currentRow.get(0)+currentRow.get(1));//记录错误用例的标题
				}
				
				
				
				sampleResult.addSubResult(subResult);
				
			}
			sampleResult.setResponseCode("200");
			sampleResult.setResponseData("正确的用例个数为："+rightSample+"\r\n错误的用例个数为："+wrongSample+"\r\n错误的用例为："+resReponse,"utf-8");
			sampleResult.setSuccessful(true);
			
			
			
			
		} catch (Exception e) {
			log.error("获取excel地址失败", e);
			sampleResult.setResponseCode("200");
			sampleResult.setResponseData("出错了"+getTrace(e),"utf-8");
			sampleResult.setSuccessful(false);
		}
		
		
		return sampleResult;
	}
	
    public  String getTrace(Throwable t) {   
    	StringWriter stringWriter= new StringWriter();   
    	PrintWriter writer= new PrintWriter(stringWriter);   
    	t.printStackTrace(writer);   
    	StringBuffer buffer= stringWriter.getBuffer();   
    	return buffer.toString();   
    }   
	



	private void trace(String s){
		String tl=this.getName();
		System.out.println(tl);
		String tn=Thread.currentThread().getName();
		String th=toString();
		System.out.println(th);
		log.debug(tn + " (" + classCount.get() + ") " + tl + " " + s + " " + th);
		  
	}


	 @Override
	    public boolean applies(ConfigTestElement configElement) {
	        String guiClass = configElement.getProperty(TestElement.GUI_CLASS).getStringValue();
	        return APPLIABLE_CONFIG_CLASSES.contains(guiClass);
	    }
	 

	 /**
	  * 获取相关配置
	  * @param sam
	  * @param configs
	  */
	  private void configureWithConfigElements(Sampler sam, List<ConfigTestElement> configs) {
	        sam.clearTestElementChildren();
	        for (ConfigTestElement config  : configs) {
	            if (!(config instanceof NoConfigMerge)) 
	            {
	                if(sam instanceof ConfigMergabilityIndicator) {
	                    if(((ConfigMergabilityIndicator)sam).applies(config)) {
	                        sam.addTestElement(config);
	                    }
	                } else {
	                    // Backward compatibility
	                    sam.addTestElement(config);
	                }
	            }
	        }
	    }
	  
  /**
   * 构建每个http请求的headManage
   * key:contentype
   * type:格式xml或json或其他
   * 
   */
	 private HeaderManager createVirtualHeaderManager(String type){
		 HeaderManager headerManager = new HeaderManager();
		 Header header =new Header("Content-Type",type);
//		 Argument argument = new Argument("Content-Type", type);
//		 Arguments arguments =new Arguments();
//		 arguments.addArgument(argument);
		 headerManager.add(header);
		 
		 
		 
		 return headerManager;
	 }
	 /**
	  * http传入基本参数，headerManager需要在整合conifg后才能继续整合
	  * configureWithConfigElements(subSample, samplePackage.getConfigs());
	  * 
	  * 下面这一步用于加入excel中的输入的值，以及一些默认的值
	  * 不用在excel中输入，另外还需要取出HeaderManager，用于将excel中的contenttype加入到其中
	  * 需要参考arguments类和argument类
	  *
	  */
	 /**
	  * 这里要重写，具体参考类httpTestSampleGui
	  * 形式改成这样：
	  * subSample.setProperty(new TestElementProperty(HTTPSamplerBase.ARGUMENTS, args));
	  * 
	  */
	 private Arguments bodyArguments;
	 private HTTPArgument httpArgument;
	 private void setSubSampleParams(){
		 bodyArguments=new Arguments();
		 subSample.setName(currentRow.get(0)+"、"+currentRow.get(1));//设置每一行的名字
		 subSample.setThreadName(jMeterContext.getThread().getThreadName());
		 subSample.setProtocol(currentRow.get(2));
		 subSample.setDomain(currentRow.get(3));
		 subSample.setPath(currentRow.get(4));
		 subSample.setMethod(currentRow.get(5));
		 
		 subSample.setPort(Integer.valueOf(currentRow.get(6)));
		 body=currentRow.get(7);
		 
		 if (null!=body &&!body.isEmpty()) {//这里是有body和没body的情况，有body则postBodyRaw为true，并传值到arguments中
			subSample.setPostBodyRaw(true);
			httpArgument = new HTTPArgument("", body, false);
			bodyArguments.addArgument(httpArgument);
			subSample.setArguments(bodyArguments);
		}
		 subSample.setFollowRedirects(true);
		 subSample.setAutoRedirects(false);
		 subSample.setUseKeepAlive(true);
		 subSample.setDoMultipartPost(false);
		// subSample.setDoBrowserCompatibleMultipart(false);
		 //注入各种配置
		 configureWithConfigElements(subSample, samplePackage.getConfigs());
		//转换headermanager1
		 if (currentRow.get(8)!=null&& !currentRow.get(8).isEmpty()) {
			 subSample.setHeaderManager(createVirtualHeaderManager(currentRow.get(8)));
			 //subSample.setCookieManager(value);
			 
		}
		 
		
	 }
	 
	 
	 /**
	  * setSubSampleParams重写
	  * @param reg
	  * @param str
	  * @return
	  * 参考这个形式，参考类httpTestSampleGui
	  * subSample.setProperty(new TestElementProperty(HTTPSamplerBase.ARGUMENTS, args));
	  * 另外还有一些第二部分如代理，超时之类的没有进行模拟，如果后续出现问题的话再进行模拟
	  */
	 HTTPArgument argument;
	 private void re_setSubSampleParams(){
		 boolean useRaw=false;
		 bodyArguments=new Arguments();
		 body=currentRow.get(7);
		 argument= new HTTPArgument("", body.replaceAll("\n","\r\n"), false);
		 argument.setAlwaysEncoded(false);
		 System.out.println("这里是请求体"+body);
		 try {
			body=new String(body.getBytes(),"utf-8");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 System.out.println("这里是请求体"+body);
		 useRaw=(!body.isEmpty()&&null!=body);
		 subSample.setName(currentRow.get(0)+"、"+currentRow.get(1));//设置每一行的名字
		 subSample.setThreadName(jMeterContext.getThread().getThreadName());
		 subSample.setProperty(HTTPSamplerBase.POST_BODY_RAW,useRaw,HTTPSamplerBase.POST_BODY_RAW_DEFAULT);
		 bodyArguments.addArgument(argument);
		 subSample.setProperty(new TestElementProperty(HTTPSamplerBase.ARGUMENTS, bodyArguments));
		 subSample.setProperty(HTTPSamplerBase.DOMAIN,currentRow.get(3));
		 subSample.setProperty(HTTPSamplerBase.PORT,currentRow.get(6));
		 subSample.setProperty(HTTPSamplerBase.PROTOCOL,currentRow.get(2));
		 subSample.setProperty(HTTPSamplerBase.CONTENT_ENCODING,"utf-8");
		 subSample.setProperty(HTTPSamplerBase.PATH,currentRow.get(4));
		 subSample.setProperty(HTTPSamplerBase.METHOD,currentRow.get(5).toUpperCase());
		 subSample.setProperty(new BooleanProperty(HTTPSamplerBase.FOLLOW_REDIRECTS, true));
		 subSample.setProperty(new BooleanProperty(HTTPSamplerBase.AUTO_REDIRECTS, false));
		 subSample.setProperty(new BooleanProperty(HTTPSamplerBase.USE_KEEPALIVE,true ));
		 subSample.setProperty(new BooleanProperty(HTTPSamplerBase.DO_MULTIPART_POST, false));
		 subSample.setProperty(HTTPSamplerBase.BROWSER_COMPATIBLE_MULTIPART, false,HTTPSamplerBase.BROWSER_COMPATIBLE_MULTIPART_MODE_DEFAULT);
		 //注入各种配置
		 configureWithConfigElements(subSample, samplePackage.getConfigs());
		//转换headermanager1
		 if (currentRow.get(0)!="") {
			
			 subSample.setHeaderManager(createVirtualHeaderManager(currentRow.get(8)));
		}
	 }
	 
	 private List<String> find(String reg, String str) {
	        Matcher matcher = Pattern.compile(reg).matcher(str);
	        List<String> list = new ArrayList<String>();
	        while (matcher.find()) {
	            list.add(matcher.group());
	        }
	        return list;
	    }
		
		
		
		 private  List<String> getKeywords(String p){
		        String reg = "(?<=(?<!\\\\)\\$\\{)(.*?)(?=(?<!\\\\)\\})";    
		       
		        List<String> list = find(reg, p);
		        return list;
		    }
		 private String restore(String key){
			 return "${"+key+"}";
		 }
		 
		 /**
		  * 正则参数化替换，将所有${}参数全部替换，实现类似jmeter的参数替换
		  * @param p
		  * @param map
		  * @return
		  */
		 public  String replaceParam(String p,JMeterVariables variables){
			 List<String> keywords = getKeywords(p);
			 String newp=p;
			 for (String key : keywords) {
				 if (variables.get(key)!=null) {
					newp = newp.replace(restore(key), variables.get(key));
					System.out.println(variables.get(key));
					System.out.println(restore(key));
				}else {
					newp=newp.replace(restore(key), "找不到此参数");
				}
				 
			}
			 return newp;
		 }
		 
		 public List<String> replaceParam(List<String> ps,JMeterVariables variables){
			 List<String> newps=new ArrayList<>();
			 for (String p : ps) {
				newps.add(replaceParam(p, variables));
			}
			 return newps;
			 
		 }
		 
		 /**
		  * 参数化正则提取，并将提出来的值放到variables中
		  * 正则提取参考类GegexExtractor
		  * 正则验证参考类（查看结果树中）RenderAsRegexp
		  */
			public  void process(String textToParse,String varName,String regexp) {

		        Perl5Matcher matcher = new Perl5Matcher();
		        PatternMatcherInput input = new PatternMatcherInput(textToParse);

		        PatternCacheLRU pcLRU = new PatternCacheLRU();
		        org.apache.oro.text.regex.Pattern pattern;
		        try {
		            pattern = pcLRU.getPattern(regexp, Perl5Compiler.READ_ONLY_MASK);
		        } catch (MalformedCachePatternException e) {
		            return ;
		        }
		        List<MatchResult> matches = new LinkedList<>();
		        while (matcher.contains(input, pattern)) {
		            matches.add(matcher.getMatch());
		        }
		        if (matches.size()>0) {
					
		        	jMeterContext.getVariables().put(varName, matches.get(0).group(1));
				}else{
					jMeterContext.getVariables().put(varName, null);
					
				}

		    }
			
			

			
			/**
			 * 根据sql和contailn来分发断言
			 */
			@SuppressWarnings("unused")
			private void assertion(SampleResult subResult,String assertionType,String assertion){
				assertionResult = new AssertionResult(subResult.getSampleLabel()+"断言");
				if (assertionType.trim().contains("contain")) {
					containAssertion(subResult, assertion);
				}else if (assertionType.trim().startsWith("sql")) {
					new JDBCAssertion().sqlAssertion(assertionResult, subResult, assertionType, assertion);
				}else if(assertionType==null||assertionType==""){
					return;
				}else{
					assertionResult.setFailure(true);
					assertionResult.setFailureMessage("没有这样的断言类型:"+assertionType);
					subResult.addAssertionResult(assertionResult);
					subResult.setSuccessful(false);
				}
				
			}
			
			
			/**
			 * 断言为contain的时间
			 * 断言要忽略状态码
			 * @param subResult
			 * @param string
			 */
			private void containAssertion(SampleResult subResult, String string) {
				
				boolean contains = subResult.getResponseDataAsString().contains(string);
				if (!contains) {
					assertionResult.setFailure(true);
					assertionResult.setFailureMessage("not find expect:"+string);
					subResult.addAssertionResult(assertionResult);
				}else{
					assertionResult.setFailure(false);
				}
				subResult.setSuccessful(contains);
			
			
		}
			
			
	
}
